﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Net;
using System.Net.Sockets;
using System.Net.NetworkInformation;
using System.IO;

using NAudio;
using NAudio.Wave;

namespace slCommunicator
{
    public class Session
    {
        private TcpClient tcpclient;
        private bool bIncoming;
        private ContactInformation me;
        private ContactInformation him;
        private StreamReader tcpreader;
        private StreamWriter tcpwriter;
        private NetworkStream tcpstream;
        private TimeSpan maxDelay = Properties.Settings.Default.tcpMaxDelay;
        public static Session currentSession;
        public bool bTerminated = false;

        public Session(TcpClient remote, bool bInc)
        {
            if (currentSession != null)
            {
                Console.WriteLine(localization.session_multisessionNotSupported);
                throw new Exception(localization.session_multisessionNotSupported);
            }
            currentSession = this;

            tcpclient = remote;
            bIncoming = bInc;

            tcpstream = tcpclient.GetStream();
            tcpreader = new StreamReader(tcpstream);
            tcpwriter = new StreamWriter(tcpstream);
            tcpclient.NoDelay = true;

            Console.WriteLine((bIncoming ? localization.session_incomingCall : localization.session_outgoingCall), remote.Client.RemoteEndPoint);
            me = ContactInformation.MyContactInfo;
            me.WriteToStream(tcpwriter);
            him = ContactInformation.ReadFromStream(tcpreader);
            Console.WriteLine(localization.session_contactExchanged, him.DisplayName);

            Recorder.NewRecording();

            new Task(CommunicateTcp).Start();
            SessionForm.Create(him);
        }

        ~Session()
        {
            Console.WriteLine(localization.session_destryCollected);
        }

        public void Terminate()
        {
            if (bTerminated) // avoid doing it twice
                return;
            bTerminated = true;

            if (tcpclient != null)
                tcpclient.Close();

            tcpclient = null;
            tcpstream = null;
            tcpreader = null;
            tcpwriter = null;
            him = null;
            me = null;
            currentSession = null;
            SessionForm.InvokeClose();
            Recorder.EndRecording();
            Console.WriteLine(localization.session_endReleaseResources);
        }

        public void SendData(byte[] data)
        {
            if (tcpclient == null)
                return;
           
            string line = DateTime.Now.ToBinary() + "@" + Convert.ToBase64String(data);
            tcpwriter.WriteLine(line);
        }

        private void CommunicateTcp()
        {
            while (tcpclient != null)
            {
                try
                {
                    if (tcpclient.Available > 0)
                    {
                        string base64line = tcpreader.ReadLine();
                        int atIndex = base64line.IndexOf("@");
                        DateTime sendTime = DateTime.FromBinary(long.Parse(base64line.Substring(0, atIndex)));
                        if (DateTime.Now.Subtract(sendTime) > maxDelay)
                        {
                            Console.WriteLine(localization.session_commDelayExceeded);
                            break;
                        }
                        byte[] packet = Convert.FromBase64String(base64line.Substring(atIndex+1));
                        DuplexAudio.PlayEncodedEncrypted(packet);
                    }
                    else
                    {
                        System.Threading.Thread.Sleep(500);
                        TcpState state = GetState(tcpclient);
                        if (state != TcpState.Established)
                            break;
                    }
                }
                catch (Exception e)
                {
                    Console.WriteLine("CommunicateTcp " + e.Message);
                    break;
                }
            }
            Console.WriteLine(localization.session_endTcpStatus);
            Terminate();
        }

        private static TcpState GetState(TcpClient tcpClient)
        {
            var foo = IPGlobalProperties.GetIPGlobalProperties()
              .GetActiveTcpConnections()
              .SingleOrDefault(x => x.LocalEndPoint.Equals(tcpClient.Client.LocalEndPoint));
            return foo != null ? foo.State : TcpState.Unknown;
        }
    }
}
